package bev.frames.client;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

import java.util.Collections;
import java.util.EnumSet;
import java.util.Vector;

import bev.ConnectionManager;
import bev.article.ArticleType;
import bev.article.BaseArticle;
import bev.query.ArticleListItem;
import bev.query.ArticleQuery;

public class SearchPane extends JPanel {
    private static final long serialVersionUID = 378448346283933959L;

    ClientFrame parentFrame;
    Vector<ArticleListItem> completeArticleList;
    JList<ArticleListItem> filteredArticleList;
    ArticleQuery lastQuery;
    JTextField searchField;

    // A search panel used to search for articles. Displays search results in a
    // list.
    public SearchPane( ClientFrame parentFrame ) {
        this.parentFrame = parentFrame;

        // Set the article list to blank for default.
        completeArticleList = new Vector<ArticleListItem>();

        setLayout( new GridBagLayout() );

        // Search controls.
        GridBagConstraints gbcSrchCtrls = new GridBagConstraints();
        gbcSrchCtrls.fill = GridBagConstraints.HORIZONTAL;
        gbcSrchCtrls.gridx = 0;
        gbcSrchCtrls.gridy = 0;
        gbcSrchCtrls.weightx = 1.0;
        gbcSrchCtrls.weighty = 0.0;
        JPanel searchControls = this.buildSearchControls();
        add( searchControls, gbcSrchCtrls );

        // Separator.
        GridBagConstraints gbcSpr1 = new GridBagConstraints();
        gbcSpr1.fill = GridBagConstraints.HORIZONTAL;
        gbcSpr1.gridx = 0;
        gbcSpr1.gridy = 1;
        JSeparator separator1 = new JSeparator();
        separator1.setOrientation( SwingConstants.HORIZONTAL );
        add( separator1, gbcSpr1 );

        // Article list.
        GridBagConstraints gbcArtList = new GridBagConstraints();
        gbcArtList.fill = GridBagConstraints.BOTH;
        gbcArtList.gridx = 0;
        gbcArtList.gridy = 2;
        gbcArtList.weightx = 1.0;
        gbcArtList.weighty = 1.0;
        filteredArticleList = new JList<ArticleListItem>();
        filteredArticleList.setBorder( new EmptyBorder( 5, 5, 5, 5 ) );
        filteredArticleList
                .setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
        filteredArticleList.addMouseListener( new ListMouseClick(
                filteredArticleList ) );
        filteredArticleList.addKeyListener( new ListEnterKey(
                filteredArticleList ) );
        add( new JScrollPane( filteredArticleList ), gbcArtList );
    }

    // Builds and returns a JPanel containing the search panel controls.
    private JPanel buildSearchControls() {
        JPanel searchControls = new JPanel();

        searchControls.setLayout( new GridBagLayout() );

        // Build and add search label.
        GridBagConstraints gbcSrchLbl = new GridBagConstraints();
        gbcSrchLbl.anchor = GridBagConstraints.WEST;
        gbcSrchLbl.insets = new Insets( 5, 10, 3, 10 );
        gbcSrchLbl.gridx = 0;
        gbcSrchLbl.gridy = 0;
        JLabel lblSearch = new JLabel( "Article Name" );
        searchControls.add( lblSearch, gbcSrchLbl );

        // Build and add search query text field.
        GridBagConstraints gbcSrchFld = new GridBagConstraints();
        gbcSrchFld.fill = GridBagConstraints.HORIZONTAL;
        gbcSrchFld.insets = new Insets( 3, 10, 3, 10 );
        gbcSrchFld.gridx = 0;
        gbcSrchFld.gridy = 1;
        gbcSrchFld.weightx = 1.0;
        gbcSrchFld.weighty = 0.0;
        searchField = new JTextField();
        searchField.addActionListener( new SearchButtonClick() );
        searchControls.add( searchField, gbcSrchFld );

        // Build and add search button.
        GridBagConstraints gbcSrchBtn = new GridBagConstraints();
        gbcSrchBtn.anchor = GridBagConstraints.EAST;
        gbcSrchBtn.insets = new Insets( 3, 10, 5, 10 );
        gbcSrchBtn.gridx = 0;
        gbcSrchBtn.gridy = 2;
        JButton btnSearch = new JButton( "Search" );
        btnSearch.addActionListener( new SearchButtonClick() );
        searchControls.add( btnSearch, gbcSrchBtn );

        return searchControls;
    }

    // Runs a query to update the search list by passing in an ArticleQuery
    // object with the search details on how to query the database.
    public void runQuery( ArticleQuery query ) {
        lastQuery = query;

        // Update the complete article list.
        completeArticleList = query.runQuery();

        // Update article search list and apply filtering.
        parentFrame.filterArticleView();
    }

    // Runs the last query again to refresh the article search list.
    public void refreshLastQuery() {
        if ( lastQuery != null )
            runQuery( lastQuery );
    }

    // Filters the complete article list in memory so that the visible article
    // list component only
    // displays articles matching the current filter. This method takes in an
    // EnumSet containing
    // the ArticleType enumerations for the articles to display (i.e. person,
    // place, etc.).
    public void filterArticleView( EnumSet<ArticleType> articleTypes ) {
        Vector<ArticleListItem> newList = new Vector<ArticleListItem>();

        // Build a new Vector for the filtered search view
        // to display based on the current filter settings.
        for ( ArticleListItem listItem : completeArticleList ) {
            if ( articleTypes.contains( listItem.getArticleType() ) )
                newList.add( listItem );
        }

        // Sort the list to be viewed.
        Collections.sort( newList );

        // Update the visible article search list.
        filteredArticleList.removeAll();
        filteredArticleList.setListData( newList );
    }

    // Handles clicking on the search button.
    class SearchButtonClick implements ActionListener {
        public void actionPerformed( ActionEvent e ) {
            if ( !ConnectionManager.isConnected() ) {
                JOptionPane
                        .showMessageDialog(
                                SearchPane.this.parentFrame,
                                "Not connected to a project database! You must be connected\nto a project database before you can search for articles." );
                return;
            }

            ArticleQuery query = new ArticleQuery( searchField.getText() );
            SearchPane.this.runQuery( query );
            searchField.setText( "" );
        }
    }

    // Handles clicking on an element in the article
    // list or selecting it with the enter key.
    class ListMouseClick extends MouseAdapter {
        JList<ArticleListItem> list;

        public ListMouseClick( JList<ArticleListItem> list ) {
            this.list = list;
        }

        public void mouseClicked( MouseEvent e ) {
            if ( e.getClickCount() == 2 ) {
                int index = list.locationToIndex( e.getPoint() );
                Object item = list.getModel().getElementAt( index );
                list.ensureIndexIsVisible( index );
                BaseArticle loadedArticle = ( (ArticleListItem) item )
                        .loadArticle();
                parentFrame.setCurrentArticle( loadedArticle );
            }
        }
    }

    // Handles clicking on an element in the article list.
    class ListEnterKey extends KeyAdapter {
        JList<ArticleListItem> list;

        public ListEnterKey( JList<ArticleListItem> list ) {
            this.list = list;
        }

        public void keyReleased( KeyEvent e ) {
            if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
                int index = list.getSelectedIndex();
                Object item = list.getModel().getElementAt( index );
                list.ensureIndexIsVisible( index );
                BaseArticle loadedArticle = ( (ArticleListItem) item )
                        .loadArticle();
                parentFrame.setCurrentArticle( loadedArticle );
            }
        }
    }
}
